GAE で Stackdriver Logging にログを拾わせてトレースしたい
#blogged 2020/4/8 Cloud Functions でログをグルーピングする - ぽ靴な缶
やりたいこと
エラーログやアプリケーションログを出したい
構造化したログを吐きたい
textPayload ではなく jsonPayload にしたい
GAE のアクセスログとアプリケーションから出力するログを対応付けたい
Stackdriver Logging のクライアントをアプリケーションから使いたくない、標準出力/標準エラー出力で済ませたい
やり方
構造化ペイロードの特殊フィールド / エージェントを構成する  |  Stackdriver Logging  |  Google Cloud
これの JSON ログフィールド をキーとした JSON を標準出力/標準エラー出力に書き出す
アプリケーションから入れるのは severity や message
アプリケーション中での時刻を入れたいなら 時間関連フィールド の構造で入れる
なくても勝手に紐づく?
trace と spanId は X-Cloud-Trace-Context リクエストヘッダに入ってやってくるのでバラしてログに入れる
X-Cloud-Trace-Context: fdddafd26d20124266fc7d4465c70806/12935805563810517035 なら
logging.googleapis.com/trace: "projects/{ProjectID}/traces/fdddafd26d20124266fc7d4465c70806"
logging.googleapis.com/spanId: "12935805563810517035"
を出力するとヒモづく
ログ例
code:log.json
{
"severity": "info",
"message": "application log",
"timestampSeconds": ...,
"timestampNanos": ...,
"logging.googleapis.com/trace": "projects/{ProjectID}/traces/fdddafd26d20124266fc7d4465c70806",
"logging.googleapis.com/spanId": "12935805563810517035",
...
}
Stackdriver Logging の表示
https://gyazo.com/60c4562910c6d245a104b404a46d5aaa
アプリケーションから標準出力に書いたログが先に出ている
* のアクセスログにアプリケーションログが紐付いている
アプリケーションログの行に出てくる文字列は message フィールド
紐付いたところから、アプリケーションログへのリンクはゲットできるが直接は飛べない
trace をクリックするとフィルタへのリンクが出てくる
その場で payload を展開できない
trace の値で検索するのが楽
Go & gin で実装する例
ミドルウェア作って Context で Trace 情報を引き回す
code:middelware.go
type middlewareContextKey string
const traceKey middlewareContextKey = "Trace"
type Trace struct {
TraceID string
SpanID string
}
// XXX 必要になってからパースする & Context にキャッシュする、という実装の方がパフォーマンス出そう
func StackdriverTrace() gin.HandlerFunc {
return func(c *gin.Context) {
trace := Trace{}
value := c.Request.Header.Get("X-Cloud-Trace-Context")
if value != "" {
params := strings.Split(value, "/")
if len(params) == 2 {
trace.TraceID = params0
trace.SpanID = params1
}
}
ctx := context.WithValue(c.Request.Context(), traceKey, trace)
c.Request = c.Request.WithContext(ctx)
c.Next()
}
}
func GetStackdriverTrace(req *http.Request) Trace {
trace, _ := req.Context().Value(traceKey).(Trace)
return trace
}
ロガーは sirupsen/logrus: Structured, pluggable logging for Go. を使う
JSON で出力する、キー名を 構造化ペイロードの特殊フィールド に合わせる
実際はエラーログとロガーインスタンスを分けたり、他のフィールドを入れれるように func 作ったり
code:logger.go
import (
...
log "github.com/sirupsen/logrus"
)
func init() {
log.SetFormatter(&log.JSONFormatter{
FieldMap: log.FieldMap{
log.FieldKeyLevel: "severity",
log.FieldKeyMsg: "message",
},
DisableTimestamp: true,
})
log.SetOutput(os.Stdout)
}
...
trace := GetStackdriverTrace(c.Request)
log.WithFields(log.Fields{
"timestampSeconds": now.Unix(),
"timestampNanos": now.Nanosecond(),
"logging.googleapis.com/trace": fmt.Sprintf("projects/%s/traces/%s", ProjectID, trace.TraceID)
"logging.googleapis.com/spanId": trace.SpanID
}).Info("application log")
参考
GAE のログに憧れて – google-cloud-jp – Medium
AppEngineの旧Log APIを脱却したい話 - Mercari Engineering Blog
GoでStackdriver Logging向けのログをお手軽に出力する設定 - YAMAGUCHI::weblog
OpenCensus + Stackdriver Trace で分散トレース上にログを表示する - YAMAGUCHI::weblog
#GAE #GoogleCloud #Go